/* CoralCube | Massively Multiplayer Online Role-Playing Game(MMORPG) Emulator.
|* 
|* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
|*               2008-2010 TrinityCore <http://www.trinitycore.org/>
|*               2008-2010 CoralCube <http://www.CoralCube.com/>
|*
|* This program is free software. You can redistribute it and/or modify it under the terms of 
|* the GNU General Public License as published by the Free Software Foundation, either 
|* version 2 of the License.
|*
|* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, 
|* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
|* See the GNU General Public License for more details.
|*
|* You should have received a copy of the GNU General Public License along with this program. 
|* If not, see <http://www.gnu.org/licenses/>.
|*/

#include <openssl/opensslv.h>
#include <openssl/crypto.h>
#include <ace/Version.h>

#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Configuration/Config.h"
#include "SystemConfig.h"
#include "Check.h"

#include "Log.h"
#include "Master.h"

#ifdef _WIN32
#include "ServiceWin32.h"
char serviceName[] = "CoralCube";
char serviceLongName[] = "CoralCube service";
char serviceDescription[] = "Massive Network Game Object Server";
/*
* -1 - not in service mode
*  0 - stopped
*  1 - running
*  2 - paused
*/
int m_ServiceStatus = -1;
#endif

WorldDatabaseWorkerPool WorldDatabase;                      ///< Accessor to the world database
CharacterDatabaseWorkerPool CharacterDatabase;              ///< Accessor to the character database
ConfigDatabaseWorkerPool ConfigDatabase;                    ///< Accessor to the config database
LoginDatabaseWorkerPool LoginDatabase;                      ///< Accessor to the realm/login database

uint32 realmID;                                             ///< Id of the realm

/// Print out the usage string for this program on the console.
void usage(const char *prog)
{
    sLog.outString("Usage: \n %s [<options>]\n"
        "    -c config_file           use config_file as configuration file\n\r"
#ifdef _WIN32
        "    Running as service functions:\n\r"
        "    --service                run as service\n\r"
        "    -s install               install service\n\r"
        "    -s uninstall             uninstall service\n\r"
#endif
        ,prog);
}

/// Launch the Core server
extern int main(int argc, char **argv)
{
    ACE::init();
    sLog.SetLogDB(false);

    ///- Command line parsing to get the configuration file name
    char const *cfg_file = _SETTINGS_FILE;
    int c = 1;

    while (c < argc)
    {
        if (strcmp(argv[c], "-c") == 0)
        {
            if (++c >= argc)
            {
                sLog.outError("Runtime-Error: -c option requires an input argument");
                usage(argv[0]);
                return 1;
            }
            else
                cfg_file = argv[c];
        }

#ifdef _WIN32
        ////////////
        //Services//
        ////////////
        if (strcmp(argv[c], "-s") == 0)
        {
            if (++c >= argc)
            {
                sLog.outError("Runtime-Error: -s option requires an input argument");
                usage(argv[0]);
                return 1;
            }

            if (strcmp(argv[c], "install") == 0)
            {
                if (WinServiceInstall())
                    sLog.outString("Installing service");

                return 1;
            }
            else if (strcmp(argv[c], "uninstall") == 0)
            {
                if (WinServiceUninstall())
                    sLog.outString("Uninstalling service");

                return 1;
            }
            else
            {
                sLog.outError("Runtime-Error: unsupported option %s", argv[c]);
                usage(argv[0]);
                return 1;
            }
        }

        if (strcmp(argv[c], "--service") == 0)
        {
            WinServiceRun();
        }
        ////
#endif
        ++c;
    }

    if (!sConfig.SetSource(cfg_file))
    {
        sLog.outError("Invalid or missing configuration file : %s", cfg_file);
        sLog.outError("Verify that the file exists and has \'[SettingConf]\' written in the top of the file!");
        return 1;
    }

    ///- Check the version of the configuration file
    sCheck.CheckConfigVersion(sConfig.GetIntDefault("ConfVersion", 0), _SETTINGS_VERSION);

    ///- Start the databases
    if (!sMaster._StartDB())
        return 1;

    ///- Read important Logging Settings
    sConfigDB.logs_dir                                         = "logs";
    sConfigDB.char_dump_dir                                    = "";
    sConfigDB.log_file                                         = "Server.log";
    sConfigDB.log_conf                                         = "Config.log";
    sConfigDB.log_sql                                          = "SQLDriver.log";
    sConfigDB.log_arena                                        = "Arena.log";
    sConfigDB.log_char                                         = "Characters.log";
    sConfigDB.log_chat                                         = "Chat.log";
    sConfigDB.log_db                                           = "DBErrors.log";
    sConfigDB.log_gm                                           = "Gamemaster.log";
    sConfigDB.log_ra                                           = "Ra.log";
    sConfigDB.log_world                                        = "World.log";
    sConfigDB.console_colors                                   = "13 8 9 5";
    sConfigDB.setBoolConfig(CONFIG_LOG_FILE_TIMESTAMP,           false);
    sConfigDB.setBoolConfig(CONFIG_LOG_CONFIG_TIMESTAMP,         false);
    sConfigDB.setBoolConfig(CONFIG_LOG_SQL_TIMESTAMP,            false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_ARENA_TIMESTAMP,    false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAR_TIMESTAMP,     false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAT_TIMESTAMP,     false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_DB_TIMESTAMP,       false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_GM_TIMESTAMP,       false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_RA_TIMESTAMP,       false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAR_DUMP,          false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAR_DUMP_SEPARATE, false);
    sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_GM_PER_ACCOUNT,     false);
    sConfigDB.setIntConfig(CONFIG_LOG_CONSOLE_LEVEL,             1);
    sConfigDB.setIntConfig(CONFIG_LOG_FILE_LEVEL,                0);
    sConfigDB.setBoolConfig(CONFIG_LOG_DB_ENABLE,                false);

    if (QueryResult qResult = ConfigDatabase.Query("SELECT name,value FROM 03_server_logging"))
    {
        Field *fields = qResult->Fetch();

        if (fields[0].GetString() == "Log.Dir")
            if (fields[1].GetString() != sConfigDB.logs_dir)
                sConfigDB.logs_dir = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.Characters.Dump.SeparateDir")
            if (fields[1].GetString() != sConfigDB.char_dump_dir)
                sConfigDB.char_dump_dir = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.World")
            if (fields[1].GetString() != sConfigDB.log_file)
                sConfigDB.log_file = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.Config")
            if (fields[1].GetString() != sConfigDB.log_conf)
                sConfigDB.log_conf = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.SqlDriver")
            if (fields[1].GetString() != sConfigDB.log_sql)
                sConfigDB.log_sql = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.Arena")
            if (fields[1].GetString() != sConfigDB.log_arena)
                sConfigDB.log_arena = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.Characters")
            if (fields[1].GetString() != sConfigDB.log_char)
                sConfigDB.log_char = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.Chat")
            if (fields[1].GetString() != sConfigDB.log_chat)
                sConfigDB.log_chat = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.DBErrors")
            if (fields[1].GetString() != sConfigDB.log_db)
                sConfigDB.log_db = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.Gamemaster")
            if (fields[1].GetString() != sConfigDB.log_gm)
                sConfigDB.log_gm = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.RemoteAccess")
            if (fields[1].GetString() != sConfigDB.log_ra)
                sConfigDB.log_ra = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.Game.World")
            if (fields[1].GetString() != sConfigDB.log_world)
                sConfigDB.log_world = fields[1].GetString();

        if (fields[0].GetString() == "Console.Colors")
            if (fields[1].GetString() != sConfigDB.console_colors)
                sConfigDB.console_colors = fields[1].GetString();

        if (fields[0].GetString() == "LogFile.World.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_LOG_FILE_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.Config.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_LOG_CONFIG_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.SqlDriver.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_LOG_SQL_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.Arena.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_ARENA_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.Characters.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAR_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.Chat.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAT_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.DBErrors.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_DB_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.Gamemaster.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_GM_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.RemoteAccess.Timestamp")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_RA_TIMESTAMP, true);

        if (fields[0].GetString() == "LogFile.Characters.Dump")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAR_DUMP, true);

        if (fields[0].GetString() == "LogFile.Characters.Dump.Separate")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_CHAR_DUMP_SEPARATE, true);

        if (fields[0].GetString() == "LogFile.Gamemaster.Per.Account")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_WORLD_LOG_GM_PER_ACCOUNT, true);

        if (fields[0].GetString() == "Console.Level")
            if (fields[1].GetUInt32() != sConfigDB.getIntConfig(CONFIG_LOG_CONSOLE_LEVEL))
                sConfigDB.setIntConfig(CONFIG_LOG_CONSOLE_LEVEL, fields[1].GetUInt32());

        if (fields[0].GetString() == "LogFile.Level")
            if (fields[1].GetUInt32() != sConfigDB.getIntConfig(CONFIG_LOG_FILE_LEVEL))
                sConfigDB.setIntConfig(CONFIG_LOG_FILE_LEVEL, fields[1].GetUInt32());

        if (fields[0].GetString() == "LogDB.Enable")
            if (fields[1].GetBool())
                sConfigDB.setBoolConfig(CONFIG_LOG_DB_ENABLE, true);
    }
    ///- Initialize log settings
    sLog.Initialize();

    ///- Check Database Required Fields
    sCheck.CheckDatabaseVersion(4);     // world
    sCheck.CheckDatabaseVersion(3);     // character
    sCheck.CheckDatabaseVersion(1);     // auth
    sCheck.CheckDatabaseVersion(2);     // config

    sLog.outString("Using configuration file %s.", cfg_file);
    
    sLog.outDetail("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));

    sLog.outDetail("Using ACE: %s", ACE_VERSION);

    ///- and run the 'Master'
    /// \todo Why do we need this 'Master'? Can't all of this be in the Main as for AuthServer?
    int ret = sMaster.Run();

    // at sMaster return function exist with codes
    // 0 - normal shutdown
    // 1 - shutdown at error
    // 2 - restart command used, this code can be used by restarter for restart WorldServer

    ACE::fini();

    return ret;
}
